package com.yuda.tools.checkanswer.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.SimplifiedObjectMeta;
import com.yuda.common.core.utils.file.FileUtils;
import com.yuda.tools.checkanswer.Oss.OssMap;
import com.yuda.tools.checkanswer.dto.*;
import com.yuda.tools.checkanswer.mapper.CheckLogMapper;
import com.yuda.tools.checkanswer.service.CheckLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
public class CheckLogServiceImpl implements CheckLogService {

    @Autowired
    private CheckLogMapper checkLogMapper;

    @Value("${check.oss_inner_out}")
    private String ossInnerOrOut;

    @Value("${check.logDownLoadPath}")
    private String logDownLoadPath;

    @Value("${check.answerDbPath}")
    private String answerDbPath;

    int checkThreadNum = 20;

    private final ExecutorService threadPool = Executors.newWorkStealingPool(checkThreadNum);
    private final Semaphore semaphore = new Semaphore(checkThreadNum);//定义几个许可

    AtomicInteger atomicInteger = new AtomicInteger(0);

    String[] area = new String[]{"beijingpaper", "huadongpaper", "huananpaper", "zhongyuanpaper", "defanswer"};

    //保存了 科目的映射关系
    Map<Integer, String> subjectMap = new HashMap<>();

    @Override
    public String checkLog(Integer paperId) throws InterruptedException {
        atomicInteger.set(0);
        List<StudentLog> studentLogs = checkLogMapper.listStudentsForLog(paperId);
        int size = studentLogs.size();
        System.out.println("需要执行的数量为： " + size);
        for (StudentLog studentLog : studentLogs) {
            System.out.println("正在处理日志第 -> " + atomicInteger.incrementAndGet());
            semaphore.acquire();
            threadPool.execute(() -> {
                try {
                    doCheckLog(studentLog, paperId);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            });
        }
        while (atomicInteger.get() < size) {
            TimeUnit.SECONDS.sleep(1);
        }
        return null;
    }

    private void fillSubjectMap() {
        List<SpartSubject> list = checkLogMapper.listSpartaSubject();
        for (SpartSubject spartSubject : list) {
            subjectMap.put(spartSubject.getSubjectId(), spartSubject.getName());
        }
    }

    @Override
    public List<LogContext> searchFromLogWithPcNo(String studentId, Integer paperId, Integer subjectId, String pcNo) {
        StudentLog studentLog = new StudentLog();
        studentLog.setPcNo(Integer.parseInt(pcNo));
        studentLog.setStudentId(studentId);

        String maxTimeArea = findMaxTimeArea(studentLog, paperId);

        if (StringUtils.isEmpty(maxTimeArea)) { //没有文件
            return Collections.singletonList(new LogContext("没有文件"));
        } else {//有文件
            //下载文件取出文件的内容
            String downLoadFileName = downLoad(paperId, studentLog, maxTimeArea);
            //解析日志返回日志的内容
            return resolveLogDb(downLoadFileName, studentId, subjectId, pcNo);
        }
    }


    @Override
    public LinkedHashMap<String, List<LogContext>> searchFromLog(String studentId, Integer paperId, Integer subjectId) {

        fillSubjectMap();
        //该考试机位集合
        List<StudentLog> studentLogs = checkLogMapper.listStudentsByStudentIdForLog(studentId, paperId);
        //把新机位放在前边
        studentLogs.sort(Comparator.comparingInt(StudentLog::getPcStatus));
        //保存日志文件的内容
        LinkedHashMap<String, List<LogContext>> logContextHashMap = new LinkedHashMap<>();

        //寻找日志文件
        for (StudentLog studentLog : studentLogs) {
            String maxTimeArea = findMaxTimeArea(studentLog, paperId);
            if (StringUtils.isEmpty(maxTimeArea)) { //没有文件
                logContextHashMap.put(String.valueOf(studentLog.getPcNo()), Collections.singletonList(new LogContext("没有文件")));
            } else {//有文件
                //下载文件取出文件的内容
                String downLoadFileName = downLoad(paperId, studentLog, maxTimeArea);
                //解析日志返回日志的内容
                List<LogContext> res = resolveLogDb(downLoadFileName, studentId, subjectId, String.valueOf(studentLog.getPcNo()));
                logContextHashMap.put(studentLog.getPcNo() + "号", res);
                //新增的添加条数的，修改了泛型
                int size = res.stream().filter(x -> x.getAction().equals("答题")).collect(Collectors.groupingBy(LogContext::getQuesId)).size();
                logContextHashMap.put(studentLog.getPcNo() + "_length", Collections.singletonList(new LogContext(String.valueOf(size))));
            }
        }
        return logContextHashMap;
    }

    private String downLoad(Integer paperId, StudentLog studentLog, String maxTimeArea) {
        OSS oss = OssMap.get(maxTimeArea + ossInnerOrOut);
        String fileName = answerDbPath + studentLog.getStudentId() + "_" + paperId + "_" + studentLog.getPcNo() + "_stat.db";
        String downLoadFileName = logDownLoadPath + "/" + studentLog.getStudentId() + "_" + paperId + "_" + studentLog.getPcNo() + "_stat.db";
        File file = new File(logDownLoadPath);
        if (!file.isDirectory()) {
            file.mkdir();
        }
        oss.getObject(new GetObjectRequest(maxTimeArea, fileName), new File(downLoadFileName));
        return downLoadFileName;
    }

    @Override
    public List<LogNotFound> listLogsNotFind(LogNotFound logNotFound) {
        return checkLogMapper.listLogNotFind(logNotFound);
    }

    @Override
    public List<String> searchCommonExamRoom(String key) {
        return checkLogMapper.searchCommonExamRoom(key);
    }

    private void doCheckLog(StudentLog studentLog, Integer paperId) {

//        String maxTimeArea = findMaxTimeArea(studentLog, paperId);
        String[] maxTimeAreaAndTime = findMaxTimeAreaAndTime(studentLog, paperId);

        //更新到数据库
        if (!StringUtils.isEmpty(maxTimeAreaAndTime[0])) {
            Integer pcStatus = studentLog.getPcStatus();
            if (pcStatus == 1) { //更新spartaExamStudent表
                checkLogMapper.updateSpartaExamStudentLogStatus(maxTimeAreaAndTime[0], studentLog.getId(), maxTimeAreaAndTime[1]);
            } else if (pcStatus == 2) { //更新sparta_change_student 表
                checkLogMapper.updateSpartaChangeStudentLogStatus(maxTimeAreaAndTime[0], studentLog.getId(), maxTimeAreaAndTime[1]);
            }
        }
    }

    @Override
    public String findMaxTimeArea(StudentLog studentLog, Integer paperId) {
        String fileName = answerDbPath + studentLog.getStudentId() + "_" + paperId + "_" + studentLog.getPcNo() + "_stat.db";

        String maxTimeArea = "";//记录了某个人最新文件所在的区域
        long maxTime = 0; //记录了某个人文件在Oss上的时间
        long existAndGetModifiedTime; //记录了某个人文件在Oss上的时间
        for (String bucket : area) {
            OSS oss = OssMap.get(bucket + ossInnerOrOut);
            if (oss.doesObjectExist(bucket, fileName)) {
                SimplifiedObjectMeta simplifiedObjectMeta = oss.getSimplifiedObjectMeta(bucket, fileName);
                existAndGetModifiedTime = simplifiedObjectMeta.getLastModified().getTime() / 1000;
                if (existAndGetModifiedTime > maxTime) {
                    maxTimeArea = bucket;
                    maxTime = existAndGetModifiedTime;
                }
            }
        }
        return maxTimeArea;
    }

    @Override
    public String[] findMaxTimeAreaAndTime(StudentLog studentLog, Integer paperId) {
        String fileName = answerDbPath + studentLog.getStudentId() + "_" + paperId + "_" + studentLog.getPcNo() + "_stat.db";

        String maxTimeArea = "";//记录了某个人最新文件所在的区域
        long maxTime = 0; //记录了某个人文件在Oss上的时间
        long existAndGetModifiedTime; //记录了某个人文件在Oss上的时间
        for (String bucket : area) {
            OSS oss = OssMap.get(bucket + ossInnerOrOut);
            if (oss.doesObjectExist(bucket, fileName)) {
                SimplifiedObjectMeta simplifiedObjectMeta = oss.getSimplifiedObjectMeta(bucket, fileName);
                existAndGetModifiedTime = simplifiedObjectMeta.getLastModified().getTime() / 1000;
                if (existAndGetModifiedTime > maxTime) {
                    maxTimeArea = bucket;
                    maxTime = existAndGetModifiedTime;
                }
            }
        }
        return new String[]{maxTimeArea, String.valueOf(maxTime)};
    }

    //执行拷贝 把日志中的文件拷贝到数据库
    @Override
    public void copy(Integer paperId,
                     String subjectId,
                     StudentLog studentLog,
                     String maxTimeArea) {

        String downLoadFileName = downLoad(paperId, studentLog, maxTimeArea);
        Connection c = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        List<LogAnswer> list = new ArrayList<>();
        try {
            Class.forName("org.sqlite.JDBC");
            c = DriverManager.getConnection("jdbc:sqlite:" + downLoadFileName);
            c.setAutoCommit(false);
//            st = c.prepareStatement("select student_id,subject_id,subject_relation_id,ques_id,paper_id,answer_abc,answer,md5 from stat a,\n" +
//                    "(select max(time),id from stat where subject_id = '" + subjectId + "' and stat_type = 2  group by ques_id ) b where a.id = b.id");
            st = c.prepareStatement("SELECT  " +
                    "  t1.student_id,t1.subject_id,t1.subject_relation_id,t1.ques_id,t1.paper_id,t1.answer_abc,t1.answer,t1.md5,t1.stat_type,t1.time " +
                    " FROM ( SELECT * from stat where stat_type = 2) t1  " +
                    "  LEFT JOIN ( SELECT * from stat where stat_type = 2) t2 ON t1.student_id = t2.student_id and t1.ques_id = t2.ques_id and t1.time||t1.id <  t2.time||t2.id  " +
                    " WHERE t2.id IS NULL ");
            rs = st.executeQuery();

            while (rs.next()) {
                LogAnswer logAnswer = new LogAnswer();
                logAnswer.setStudentId(rs.getString(1));
                logAnswer.setSubjectId(rs.getInt(2));
                logAnswer.setSubjectRelationId(rs.getInt(3));
                logAnswer.setQuesId(rs.getInt(4));
                logAnswer.setPaperId(rs.getInt(5));
                logAnswer.setAnswer(rs.getString(6));
                logAnswer.setAnswerJson(rs.getString(7));
                logAnswer.setMd5(rs.getString(8));
                list.add(logAnswer);
            }

            checkLogMapper.batchSaveAnswer(list);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }

                if (st != null) {
                    st.close();
                }

                if (c != null) {
                    c.close();
                }

                File file = new File(downLoadFileName);
                if (file.exists()) file.delete();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    private List<LogContext> resolveLogDb(String fileName, String studentId, Integer subjectId, String pcNo) {

        Connection c = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        List<LogContext> list = new ArrayList<>();
        try {
            Class.forName("org.sqlite.JDBC");
            c = DriverManager.getConnection("jdbc:sqlite:" + fileName);
            c.setAutoCommit(false);
            st = c.prepareStatement("SELECT `time`,subject_id,ques_id,case stat_type when 0 then '登录' when 1 then '开考' when 2 then '答题' when 5 then '交卷' when 6 then '补时' end  as actionOper, answer_abc,subject_relation_id from stat where subject_id = '" + subjectId + "' and stat_type in (0,1,2,5,6)");
            rs = st.executeQuery();
            while (rs.next()) {
                LogContext logContext = new LogContext();
                logContext.setTime(rs.getString(1));
                logContext.setSubjectName(subjectMap.get(rs.getInt(2)));
                logContext.setQuesId(rs.getString(3));
                logContext.setAction(rs.getString(4));
                logContext.setAnswer(rs.getString(5));
                logContext.setMessage("");
                logContext.setStudentId(studentId);
                logContext.setSubjectId(String.valueOf(subjectId));
                logContext.setSubjectRelationId(rs.getString(6));
                logContext.setPcNo(pcNo);
                list.add(logContext);
            }
        } catch (Exception e) {
            e.printStackTrace();
            list.add(new LogContext("文件解析出错"));
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }

                if (st != null) {
                    st.close();
                }

                if (c != null) {
                    c.close();
                }

                FileUtils.deleteFile(fileName);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return list;
    }
}
